In [ ]:
%pylab inline
from ekstra_funktioner import *
pylab.rcParams['figure.figsize'] = (10.0, 6.0)
Som du måske opdagede i forrige opgave, kan det være ret svært at gætte den rigtige type funktion, når man vil finde ud af, hvor ens data kommer fra.
Tag eksempelvis opgaven med at bestemme et polynomiums orden. Når vi vælger en høj orden, får vi et bedre fit - men vælger vi for høj en orden, vil funktionen lave nogle enorme udsving, og det virker ikke umiddelbart sandsynligt. Så hvor højt tør vi gå? Og hvad hvis vores data har så mange dimensioner, at vi ikke engang kan plotte dem og få en idé om strukturen?
Machine learning (ML) kan hjælpe os i disse situationer. Ved hjælp af nogle "intelligente" metoder fra ML kan vi helt automatisk finde en god model for vores data.
Vi vil her bruge ML til automatisk at finde et polynomium, der bedst muligt passer til dataene fra før. Metoden en rimelig intelligent, og vil forsøge holde polynomiet relativt simpelt, uanset hvor mange ordener vi siger, at polynomiet skal være.
Lad os f.eks. vælge, at metoden skal fitte et polynomium af orden 10. Her havde metoden fra før allerede problemer. Prøv at køre nedenstående kode og se, hvor godt den klarer det. (Vær opmærksom på, at det godt kan tage nogle sekunder før grafen kommer frem - computeren laver ret mange beregninger!).
In [ ]:
x = linspace(-10,10,200) # Vi laver igen 200 x-værdier.
plot_ml_polynomium(x, orden=10)
Prøv at sammenligne størrelsen af koefficienterne med dem, du fik i den tidligere opgave. Hvad ser du? Giver det mening?
Prøv at ændre ordenen i koden og se, hvordan det påvirker grafen. Hvor langt kan du gå op, før fittet bliver rigtig dårligt? Hvad sker der med størrelsen på koefficienterne her?
In [ ]:
plot_ml_polynomium(x, orden=...)
Lad os prøve endnu en ML metode! Denne tager lidt længere tid at beregne, men giver ofte bedre resultater.
Hvordan klarer metoden sig i forhold til den fra før? Hvordan ser koefficienterne ud her? Giver det mening?
Bemærk: Du får muligvis nogle "warnings", når du kører koden. Der er helt normalt: metoden fortæller dig bare, at den ikke har kørt længe nok til at få en løsning, den er tilfreds med. Hvis du vil, kan du prøve at skrue op for antallet af iterationer ved at skrive max_iter=10000
(eller et andet højt tal) efter orden=...
. Nu kommer det til at tage meget længere tid at beregne, og løsningen bliver ikke nødvendigvis bedre, så det er ikke sikkert, at det kan betale sig.
In [ ]:
plot_ml_polynomium2(x, orden=5)
Vi skal nu se på en anden type ML-metode, en såkaldt ikke-parametrisk metode. Det betyder, at vi ikke selv behøver at gætte på en funktion, som vi gjorde før - metoden vil helt af sig selv finde ud af, hvordan dataene ser ud.
Metoden, vi ser på her, kaldes $k$ nearest neighbours (eller bare $k$-NN). Navnet kommer af, at metoden bruger de $k$ nærmeste datapunkter ("naboer") til at bestemme, hvad værdien i et givent punkt bør være. Man kan bruge disse $k$ naboer på forskellige måder, når man skal bestemme en værdi. Den mest simple måde er blot at tage gennemsnittet af naboernes værdier, men man kan f.eks. også bruge et vægtet gennemsnit, hvor punkter, der ligger længere væk, vil tælle mindre.
$k$-NN har én parameter, der skal vælges: antallet af naboer, $k$. Med koden herunder, kan du afprøve forskellige værdier af $k$ og se, hvad der sker.
Hvad sker der, når $k$ er lille? Hvad når $k$ er stor? Giver denne opførsel mening?
In [ ]:
plot_interaktiv_kNN(x)
Du kan også prøve at ændre den metode, gennemsnittet af naboerne beregnes med. "uniform
" vil bruge et helt almindeligt gennemsnit, mens "distance
" vil vægte gennemsnittet med afstanden til punkterne. Giver grafen stadig mening, når du ændre metoden, gennemsnittet beregnes med?
Der er også andre måder, man kan beskrive sine data på, end blot ved at fitte end funktion til dem. Nogle gange vil man f.eks. gerne finde grupperinger eller klynger af data.
$K$-means clustering er en ML-metode, der netop prøver at finde klynger af datapunkter. Metoden er ret simpel, men virker ofte overraskende godt - faktisk er det ofte den metode, der bliver brugt, når f.eks. store virksomheder vil finde bestemte typer af kunder. Desværre er metoden ikke intelligent nok til at selv at finde det "rigtige" antal klynger, så det må man hjælpe den med selv.
Lad os som eksempel prøve at finde klynger i farverne i et billede:
In [ ]:
billede = Billede("http://th02.deviantart.net/fs71/PRE/f/2014/264/b/5/after_the_storm_by_joelbelessa-d801fsx.jpg")
imshow(billede.billede)
Umiddelbart vil vi nok forvente at finde nogle klynger, der representerer grøn, blå og gul (som er en blanding af rød og grøn).
Lad os prøve at plotte de enkelte pixels' farver mod hinanden, så vi kan se hvordan farverne ligger fordelt. Da hver pixel består af tre farver (rød, grøn og blå), skal vi bruge et 3D-plot for at kunne vise punkterne. Dog har billedet så mange pixels, at computeren vil få svært ved at tegne dem, så vi plotter i stedet farvne mod hinanden med konturplots (bemærk at farveskalaen er logaritmisk, da man ellers ikke ville kunne se farver, der kun tilhørte nogle få pixels). Værdierne på akserne angiver hvor "mættet", farverne er. 0 betyder at farven slet ikke er der, mens 255 er fuldt mættet. Sort vil være $(r,g,b) = (0,0,0)$, mens hvid vil være $(r,g,b) = (255,255,255)$.
In [ ]:
plot_farver(billede)
Hvor mange klynger synes du, du kan se i de tre ovenstående plots?
Følgende kode vil lave en $K$-means clustering af det tre-dimensionelle farverum (dimensionerne er "rød", "grøn" og "blå"). Prøv at angive nogle forskellige antal klynger og se hvilke, metoden finder. (Igen laver computeren en hel del beregninger, så der kan godt gå nogle sekunder, før du ser noget).
Giver de fundne klynger mening? (De røde krydser angiver klyngernes centre).
In [ ]:
plot_kMeans(billede, klynger=5)
De fundne klynger representerer de mest "gennemsnitlige" farver i billedet. Hvis vi derfor ændrer farven for hver enkelt pixel til farven af den klynge, pixlen ligger tættest på, kan vi effektivt reducere antallet af farver i billedet. Nedenstående kode vil gøre lige præcis dette, og vise dig det resulterende billede. Prøv at teste med et forskelligt antal klynger.
In [ ]:
plot_reducerede_farver(billede, klynger=5)
Prøv at sammenligne med det originale billede. Ligner de fundne farve-klynger virkelig gennemsnitsfarverne i billedet?
Når man arbejder med rigtige problemstillinger indenfor dataanalyse, kommer man ofte ud for, at ens datapunkter har hundredevis, hvis ikke tusindvis, af dimensioner. Disse dimensioner kunne være målinger af elementarpartikler i flere forskellige detektorer, basepar i en DNA-sekvens, informationer om en kunde eller noget helt syvende. Ofte vil man dog gerne vide hvilke dimensioner, der har størst betydning for en bestemt problemstilling. Måske vil man gerne vide, hvilke DNA-basepar, der ofte forekommer ved en bestemt type sygdom, eller også vil man bare gerne vide, hvad en kunde virker mest interesseret i.
I machine learning jargon kaldes disse dimensioner for features, og der er skrevet tykke bøger om, hvordan man finder de mest betydningsfulde features - såkaldt feature selection. Vi skal nu prøve kræfter med en af de bedste metoder til feature selection, nemlig random forests.
Som datasæt vil vi bruge listen over passagerer på Titanic, og så vil vi prøve at finde de features, der betyder mest i forhold til, om en person ville overleve ulykken eller ej.
In [ ]:
data = titanic_data()
#data # Fjern udkommenteringen for denne linje, hvis du vil se en tabel med dataene.
Vi vil kun beskæftige os med fem features fra datasættet:
pclass
: om passageren rejse på 1., 2. eller 3. klasse.sex
: om passageren var mand eller kvinde.age
: passagerens alder.fare
: hvor meget passageren havde betalt for billetten.who
: om passageren var voksen mand, voksen kvinde eller barn.Til sidst inkluderer vi også information om, hvorvidt passageren overlevede ("survived
"), da vores metode skal bruge dette til at træne sig selv ud fra.
Overvej hvilken rækkefølge af disse features, du tror er vigtigst for, om man overlevede Titanic-ulykken.
Lad os trække disse features ud af datasættet:
In [ ]:
features = ["pclass","sex","age","fare","who","survived"]
subset = udvaelg_features(data, features)
#subset # Fjern udkommenteringen for denne linje, hvis du vil se en tabel med dataene.
Lad os nu bruge vores random forest-metode og se, hvad der er vigtigst for at overleve. Resultatet vises i et søjlediagram - jo højere søjlen er, jo vigtigere er den givne feature for om man overlever eller ej.
In [ ]:
plot_feature_selection(subset, features)
Kig på søjlediagrammet - ser rækkefølgen ud, som du troede? Giver den mening?
Random forests er kun én metode til at lave feature selection - der findes mange, mange andre. Siden der findes så mange andre, hvor meget tror du så, at man kan stole på ovenstående rækkefølge?
Hvor meget kunne du overbevise dig selv om, at rækkefølgen gav god mening? Har du ændret opfattelse nu?
Og hvad fortæller det dig om, hvor forsigtig man skal være, når man tolker sine resultater?